Call a function using NSTimer - ios

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()
}
}
}

Related

Why is my app not working in background mode but foreground it is working?

I want to create an app as when a particular time comes it calls an API. I have done in it the foreground, but when the app is in the background, it is not executing. How can I solve the issue?
My code is below:
#objc func runCode() {
print("runcode")
timeLabel.text = "Pls select time"
}
#IBAction func dateChange(_ sender: UIDatePicker) {
if (timer != nil) {
timer.invalidate()
}
print("print \(sender.date)")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm E, d MMM y"
let somedateString = dateFormatter.string(from: sender.date)
print(somedateString)
timer = Timer(fireAt: sender.date, interval: 0, target: self, selector: #selector(runCode), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: .common)
timeLabel.text = "api will trigger at \(somedateString)"
}
#IBAction func switchAction(_ sender: UISwitch) {
if stateSwitch.isOn {
date.isHidden = false
print("The Switch is on")
timeLabel.text = "Pls select time"
} else {
date.isHidden = true
if (timer != nil) {
timer.invalidate()
}
timeLabel.text = "Timer not activated"
print("Timer not activated")
}
}
If you want to call api from background, the thread must be in background first.
Morever, you can register background fetch : https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh
You can't. You can register for background processing so that your app is allotted a little time in which to do some work. You cannot control when that time allotment will arrive though. It can be as frequent as every 20 minutes or so but it can also be much longer.

How to do timeIntervalSinceNow in Swift3?

I was making a notification and watching a tutorial on it and when I typed in:
notification.fireDate = NSDate(timeIntervalSinceNow: 0)
it says
Argument labels '(timeIntervalSinceNow:)' do not match any available
overloads
How do I fix this? Here is my code:
import UIKit
import UserNotifications
class ViewController: UIViewController {
var timer = Timer()
var time = 10
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: Selector(("Notification")), userInfo: nil, repeats: true)
// Do any additional setup after loading the view, typically from a nib.
}
func notification() {
time -= 1
if time <= 0 {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = NSDate(timeIntervalSinceNow: 0)
UIApplication.shared.scheduleLocalNotification(notification)
timer.invalidate()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func pushNotification(_ sender: AnyObject) {
let AlertView = UIAlertController(title: "Time for your call!", message: "Press go to continue", preferredStyle: .alert)
AlertView.addAction(UIAlertAction(title: "Go", style: .default, handler: nil))
self.present(AlertView, animated: true, completion: nil)
}
}
Swift 3
let minute:TimeInterval = 11.0 * 60.0;
Date(timeIntervalSinceNow: minute);
for +11 minutes to now.
If you want the fire date to be now, it's just:
notification.fireDate = Date()
As Leo noted elsewhere, the selector is incorrect. It should be:
weak var timer: Timer?
var time = 10
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
}
func handleTimer(_ timer: Timer) {
time -= 1
if time <= 0 {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = Date()
UIApplication.shared.scheduleLocalNotification(notification)
timer.invalidate()
}
}
You then asked:
that definitely works but now I don't get a notification when I leave the app
You don't get the notification when you leave your app, because the Timer doesn't continue to fire when the app isn't running. It's better to eliminate the timer altogether and just immediately schedule the local notification for the desired time. For example, to schedule a local notification to fire in 10 seconds:
override func viewDidLoad() {
super.viewDidLoad()
scheduleLocalNotification(delay: 10)
}
func scheduleLocalNotification(delay: TimeInterval) {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = Date().addingTimeInterval(delay)
UIApplication.shared.scheduleLocalNotification(notification)
}

Swift: how to schedule local notification after some seconds

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?

Cancel a local notification and schedule a new one

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);
}
})

Widget data doesn't update when scrolling - Swift

I have a widget with data. When I launch today extension my widget data is updating and showing in real time. But when I scroll the notification center and return to my widget, it doesn't update. I tried several different methods but they didn't help me. I wrote below the last method which I tried.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateLabels", userInfo: nil, repeats: true)
}
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(.NewData)
}
func updateLabels() {
runtimeLabel.text = returnTime() + " " + returnDay()
}
func returnTimeInterval() -> NSTimeInterval {
let uptime = NSProcessInfo().systemUptime
return uptime
}
func returnTime() -> String {
dateFormatter.unitsStyle = .Short
dateFormatter.allowedUnits = [.Day, .Hour, .Minute, .Second]
dateFormatter.zeroFormattingBehavior = .Pad
let time = dateFormatter.stringFromTimeInterval(returnTimeInterval())!
return time
}
func returnDay() -> String {
dateFormatter.unitsStyle = .Short
dateFormatter.allowedUnits = [.Year, .Month, .Day]
dateFormatter.zeroFormattingBehavior = .Pad
let date = NSDate(timeInterval: -returnTimeInterval(), sinceDate: NSDate())
let formatter = NSDateFormatter()
formatter.locale = NSLocale.currentLocale()
formatter.dateStyle = .MediumStyle
let megaDate = formatter.stringFromDate(date)
return megaDate
}
I tried the same and it worked with the code below:
#IBOutlet weak var infoLabel: UILabel!
var timer = NSTimer()
var counter = 0
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateLabel"), userInfo: nil, repeats: true)
}
func updateLabel(){
counter += 1
myLabel.text = "Test \(counter)"
}
Update:
viewWillAppear and viewDidAppear should be called after when the widget is active and viewDidDisappear should be called when scrolling (leaving the widget). As for now the viewDidDisappear is working as expected but not viewWillAppear and viewDidAppear on scrolling.
It´s a known bug that this is not working properly, you can read more information in this post at Apples forum and check the bug status report here.
You should write your update code in widgetPerformUpdateWithCompletionHandler method.

Resources