I want my app to schedule a local notification after some seconds it has entered in the background or inactive mode. This is whatIhave done in my view controller, when app enters background mode from it:
class HomeViewController: UITableViewController {
override func viewDidLoad() {
...
let notificationCenter:NSNotificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: #selector(self.appMovedToBackground), name: UIApplicationWillResignActiveNotification, object: nil)
}
func appMovedToBackground() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
for userInfo in appDelegate.userInfoNotificationPending {
let localNotification = UILocalNotification()
localNotification.userInfo = userInfo
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = userInfo["aps"]!["alert"] as? String
localNotification.fireDate = nil
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 2 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
UIApplication.sharedApplication().applicationIconBadgeNumber += 1
}
}
}
}
why doesn't it work?
The didReceiveLocalNotification method in app delegate is called at once as soon as the app enters background. Where am I making the mistake?
Related
So as the code shown below, the function loadView 3 is supposed to run 300 seconds(5 minutes) after the user tapped the button. But when I build and run, it does not. I also did a few experiments, I changed the timer to 5 seconds, it worked. So after the app is suspended from iOS system, the NSTimer doesn't run anymore? So what's the problem, how can I fix it?
#IBAction func buttonTapped(sender: AnyObject) {
NSTimer.scheduledTimerWithTimeInterval(300, target: self, selector: #selector(ViewController.loadView3), userInfo: nil, repeats: false)
createLocalNotification()
}
func createLocalNotification() {
let localnotification = UILocalNotification()
localnotification.fireDate = NSDate(timeIntervalSinceNow: 300)
localnotification.applicationIconBadgeNumber = 1
localnotification.soundName = UILocalNotificationDefaultSoundName
localnotification.alertBody = "Hello!"
UIApplication.sharedApplication().scheduleLocalNotification(localnotification)
}
func loadView3() {
label.text = "e89saa"
}
You could try something like this. The approach is, if timer works proceed with same logic, otherwise (probably app is killed or went to background), save firedDate and update UI before showing controller in method viewWillAppear.
#IBAction func buttonTapped(sender: AnyObject) {
NSTimer.scheduledTimerWithTimeInterval(300, target: self, selector: #selector(ViewController.loadView3), userInfo: nil, repeats: false)
createLocalNotification()
}
func createLocalNotification() {
let localnotification = UILocalNotification()
localnotification.fireDate = NSDate(timeIntervalSinceNow: 300)
localnotification.applicationIconBadgeNumber = 1
localnotification.soundName = UILocalNotificationDefaultSoundName
localnotification.alertBody = "Hello!"
UIApplication.sharedApplication().scheduleLocalNotification(localnotification)
// save in UserDefaults fireDate
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(localnotification.fireDate, forKey: "firedDate")
defaults.synchronize()
}
func loadView3() {
// reset in UserDefaults fireDate
let defaults = NSUserDefaults.standardUserDefaults()
defaults.removeObjectForKey("firedDate")
defaults.synchronize()
label.text = "e89saa"
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated) // No need for semicolon
// retrieve fireDate from UserDefaults
let defaults = NSUserDefaults.standardUserDefaults()
let fireDate = defaults.objectForKey("firedData")
// check if we should update UI
if let _ = fireDate as? NSDate! {
if currentDate.compare(firedDate) == NSComparisonResult.OrderedDescending {
loadView3()
}
}
}
On pressing a button on the local notification, I want to be able to open a specific view controller depending on some information in the notification. How can this be done?
In AppDelegate.swift
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
if let options = launchOptions {
if let notification = options[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let userInfo = notification.userInfo {
let type = userInfo["TYPE"] as! String
// do something neat here
if (type == "SLEEP") {
} else if (type == "STRESS") {
} else {
}
}
}
}
return true
}
Setting the notifications in one of the view controllers
let sleepInPrefs = prefs.valueForKey(key) as? NSDate
if sleepInPrefs != nil {
print(sleepInPrefs)
print("Setting stress notif")
for notification in (UIApplication.sharedApplication().scheduledLocalNotifications )! {
if (notification.userInfo!["TYPE"]) != nil {
if (notification.userInfo!["TYPE"] as! String == key) {
UIApplication.sharedApplication().cancelLocalNotification(notification)
print("deleting notif")
break
}
}
}
let notification = UILocalNotification()
notification.fireDate = sleepInPrefs
notification.repeatInterval = NSCalendarUnit.Day
notification.timeZone = NSCalendar.currentCalendar().timeZone
notification.alertBody = ""
notification.hasAction = true
notification.alertAction = "open"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["TYPE": key ]
notification.category = "PROMPT"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
You can pass redirection params and additional info in userInfo of UILocalNotification.
// In iOS 8.0 and later, your application must register for user notifications using -[UIApplication registerUserNotificationSettings:] before being able to schedule and present UILocalNotifications
func registerForLocaleNotifications()
{
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge , .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
func scheduleLocalNotification()
{
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 20)//will be fired in 20 seconds
notification.timeZone = NSTimeZone.defaultTimeZone()
notification.soundName = UILocalNotificationDefaultSoundName
notification.alertBody = "Test UILocalNotification"
notification.userInfo = ["TYPE":"Page1"]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification)
{
if ( application.applicationState == UIApplicationState.Active)
{
print("Active")
// App is foreground and notification is recieved,
// Show a alert.
}
else if( application.applicationState == UIApplicationState.Background)
{
print("Background")
// App is in background and notification is received,
// You can fetch required data here don't do anything with UI.
}
else if( application.applicationState == UIApplicationState.Inactive)
{
print("Inactive")
// App came in foreground by used clicking on notification,
// Use userinfo for redirecting to specific view controller.
self.redirectToPage(notification.userInfo)
}
}
func redirectToPage(userInfo:[NSObject : AnyObject]!)
{
var viewControllerToBrRedirectedTo:UIViewController!
if userInfo != nil
{
if let pageType = userInfo["TYPE"]
{
if pageType as! String == "Page1"
{
viewControllerToBrRedirectedTo = UIViewController() // creater specific view controller
}
}
}
if viewControllerToBrRedirectedTo != nil
{
if self.window != nil && self.window?.rootViewController != nil
{
let rootVC = self.window?.rootViewController!
if rootVC is UINavigationController
{
(rootVC as! UINavigationController).pushViewController(viewControllerToBrRedirectedTo, animated: true)
}
else
{
rootVC?.presentViewController(viewControllerToBrRedirectedTo, animated: true, completion: { () -> Void in
})
}
}
}
}
I have a problem. I have local notifications in my project, here is the code (thanks #leoDabus):
import UIKit
class ViewController: UIViewController {
#IBOutlet var datePicker: UIDatePicker!
#IBOutlet var notificationSwitch: UISwitch!
let localNotification = UILocalNotification()
override func viewDidLoad() {
super.viewDidLoad()
setUpNotificationsOptions()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
guard let loadedDate = NSUserDefaults().dateForKey("datePicker") else { return }
datePicker.setDate(loadedDate, animated: false)
}
func setUpNotificationsOptions() {
datePicker.datePickerMode = .Time
localNotification.timeZone = NSTimeZone.localTimeZone()
localNotification.repeatInterval = .Day
localNotification.alertAction = "Open App"
localNotification.alertBody = news[0].titleNews
localNotification.soundName = UILocalNotificationDefaultSoundName
}
func toggleNotification() {
if notificationSwitch.on {
localNotification.fireDate = datePicker.date.fireDate
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
} else {
localNotification.fireDate = nil
UIApplication.sharedApplication().cancelLocalNotification(localNotification)
}
}
#IBAction func toggleSwitch(sender: UISwitch) {
toggleNotification()
}
#IBAction func dateChanged(sender: UIDatePicker) {
NSUserDefaults().setDate(sender.date, forKey: "datePicker")
toggleNotification()
} }
extension NSUserDefaults {
func setDate(date: NSDate, forKey:String) {
NSUserDefaults().setObject(date, forKey: forKey)
}
func dateForKey(string:String) -> NSDate? {
return NSUserDefaults().objectForKey(string) as? NSDate
}}
the problem is that my local notification alert Body news[0].titleNews is the result of a parser and it's a value that changes periodically. But with the code above i obtain every day the same string, not the updated one. There is a method to have everyday the updated news? Can I cancel last scheduled notification programmatically and scheduling new one?
Set a unique id in the userInfo property of UILocalNotification and when you get the updated status/news, traverse throught all the scheduled notifications , get your notification with that id you set earlier and now do your work accordingly.
Set your id like the following
localNotification.userInfo = ["notificationID" : "Your Id"]
Code for traversing scheduled notifications
let notificationArr:NSArray? = UIApplication.sharedApplication().scheduledLocalNotifications
notificationArr!.enumerateObjectsUsingBlock({ object, index, stop in
let notification = object as! UILocalNotification;
let userInfo = notification.userInfo! as NSDictionary
let notificationID = userInfo["notificationID"] as! String
if(notificationID == "Your set Id"){
UIApplication.sharedApplication().cancelLocalNotification(notification);
}
})
I am looking to fire local notifications. I have tried to create this and I was successful, there were no errors but when I run the app in the simulator local notifications don't execute
code in app delegate
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Badge | .Sound, categories: nil))
// play default sound
return true
}
and the view controller
class TechByteSchedulingViewController:UIViewController {
#IBOutlet weak var datePicker: UIDatePicker!
#IBAction func DateChosen(sender: UIButton) {
func sendNotification(sender: UIButton) {
var localNotification = UILocalNotification()
localNotification.fireDate = datePicker.date
localNotification.repeatInterval = .CalendarUnitDay
localNotification.alertBody = "check out your daily byte"
localNotification.alertAction = "Open"
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
localNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
application.applicationIconBadgeNumber = 0
}
self.navigationController?.popToRootViewControllerAnimated(true)
}
}
override func viewDidDisappear(animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Base on what I see you implement some function inside of the ButtonAction function which is wrong... you should implement the sendNotficationfunction outside of ButtonAction then called it inside of ButtonAction
class TechByteSchedulingViewController:UIViewController {
#IBOutlet weak var datePicker: UIDatePicker!
#IBAction func DateChosen(sender: UIButton) {
self.sendNotification()
}
func sendNotification() {
var localNotification = UILocalNotification()
localNotification.fireDate = datePicker.date
localNotification.repeatInterval = .CalendarUnitDay
localNotification.alertBody = "check out your daily byte"
localNotification.alertAction = "Open"
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
localNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
application.applicationIconBadgeNumber = 0
self.navigationController?.popToRootViewControllerAnimated(true)
}
}
I use this tutorial for notifications in my app http://www.appcoda.com/local-notifications-ios8/
Each notification has action - Edit. I add Observer for it, in viewDidLoad() method:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleModifyListNotification", name: "modifyListNotification", object: nil)
When press button Edit in notification, app call this method:
func handleModifyListNotification() {
}
for assign notification I use:
func scheduleLocalNotification() {
var dateComp:NSDateComponents = NSDateComponents()
dateComp.year = 2015;
dateComp.month = 02;
dateComp.day = 24;
dateComp.hour = 14;
dateComp.minute = 34;
dateComp.timeZone = NSTimeZone.systemTimeZone()
var calender:NSCalendar? = NSCalendar(calendarIdentifier: NSGregorianCalendar)
var date:NSDate = calender!.dateFromComponents(dateComp)!
var localNotification = UILocalNotification()
localNotification.fireDate = fixNotificationDate(date)
localNotification.alertBody = "Hey, you must go shopping, remember?"
localNotification.alertAction = "View List"
localNotification.category = "shoppingListReminderCategory"
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
But how can I send data to this method to know which notification call this method?
Declare your notification like following,
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleModifyListNotification:", name: "modifyListNotification", object: nil)
with colon in Selector.
and Post your notification like,
NSNotificationCenter.defaultCenter().postNotificationName("modifyListNotification", object: "Your Object Value")
and your function should be,
func handleModifyListNotification(notification: NSNotification) {
NSLog("Object is %#", notification.valueForKey("object") as String!)
}