In my app, i have implemented functionality to check app version using bundleVersion String. Now, i want to run this function everyday at 8:00 a.m. This is kiosk based app which does not go into background. So, app would be active all the time.
I am using UILocalnotification to schedule a notification for that time. Now, my app has other UILocalnotification as well. I am not sure how can i identify notifications in app delegate didReceiveLocalNotification() method.
My method to schedule notification is below
func scheduleNotification() {
//UIApplication.sharedApplication().cancelAllLocalNotifications()
let notif = UILocalNotification()
let calendar = NSCalendar.currentCalendar()
let date = NSDate()
var calendarComponents = NSDateComponents()
calendarComponents = calendar.components([.Day,.Month,.Year], fromDate: date)
let day = calendarComponents.day
let month = calendarComponents.month
let year = calendarComponents.year
calendarComponents.day = day
calendarComponents.month = month
calendarComponents.year = year
calendarComponents.hour = 8
calendarComponents.second = 0
calendarComponents.minute = 0
calendar.timeZone = NSTimeZone.systemTimeZone()
let dateToFire = calendar.dateFromComponents(calendarComponents)
notif.fireDate = dateToFire
notif.timeZone = NSTimeZone.systemTimeZone()
notif.repeatInterval = NSCalendarUnit.NSWeekdayCalendarUnit
UIApplication.sharedApplication().scheduleLocalNotification(notif)
}
Any idea would be appreciated.
Following method could help you to execute any task at regular interval , i used this method to call webservice at regular interval to provide searching functionality :
let debounceTimer : NSTimer?
func test() {
if let timer = debounceTimer {
timer.invalidate()
}
debounceTimer = NSTimer(timeInterval: 0.3, target: self, selector: Selector("putmethodnamewhichneedstocall"), userInfo: nil, repeats: false)
NSRunLoop.currentRunLoop().addTimer(debounceTimer!, forMode: "NSDefaultRunLoopMode")
}
For specific time daily Refer to this SO link :Repeating local notification daily at a set time with swift
Hope it helps.
Related
Let's say I have a view controller with code that I want to execute at a certain time, say 2:00pm. What I want to happen is that if the user opens the view controller at 1:58pm, that the code will wait and continuously check the time, executing itself at exactly 2:00pm. Is there a way to do this in Swift 4, perhaps with a timer?
You can go around this way to achieve the result:
override func viewDidLoad() {
super.viewDidLoad()
let dateFormatter = DateFormatter()
/*The accepted DateFormat*/
dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
/*My Date String to run the code at*/
let dateString = "12-06-2017 15:41"
let date = dateFormatter.date(from: dateString)
/*Now find the time differnce from now*/
let secondsFromNowToFinish = date?.timeIntervalSinceNow
DispatchQueue.main.asyncAfter(deadline: .now() + secondsFromNowToFinish!, execute: {
print ("Hello Future.")
})
}
Tested and running.
References:
DateFormatter
Convert Date String to Date
Happy coding. Hope it helps. Please remove the forced unwrappings.
You can figure out the number of seconds between your future date and now and use either GCD or Timer to setup your future event:
let futureDate = ISO8601DateFormatter().date(from: "2018-1-1T00:00:00Z" )!
DispatchQueue.main.asyncAfter(deadline: .now() + futureDate.timeIntervalSinceNow) {
print("Its the future!")
}
or
let futureDate = ISO8601DateFormatter().date(from: "2018-1-1T00:00:00Z" )!
let timer = Timer.scheduledTimer(withTimeInterval: futureDate.timeIntervalSinceNow, repeats: false) {
print("Its the future!")
}
Timer is easier to cancel and reschedule:
timer.invalidate()
You can add a timer with a fire date to the runloop:
{
let dateOfExecution = Date(timeIntervalSince1970: <number of seconds from 1970-01-01-00:00:00 to your date of execution>)
let timer = Timer(fireAt: dateOfExecution, interval: 0, target: self, selector: #selector(codeToExecute), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
}
#objc func codeToExecute() {
<Your code to run>
}
See Apple docs: https://developer.apple.com/documentation/foundation/timer/1415700-init
Read about DispatchQueue
Example:
// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
// your code
}
Note: Your application must be active.
Also you may have a look at Background modes tutorial
I just want to ask is this even possible?
I've made simple method which as I suspect will fire notification every 1st day of month at 12:00
What I want to do is fires notifications 1st,2nd,3rd day of every month at for ex. 8:00 am and 12:00
func scheduleLocalNotification() {
let calendar = NSCalendar.autoupdatingCurrent
var calendarComponents = DateComponents()
calendarComponents.day = 1
calendarComponents.hour = 12
calendarComponents.minute = 00
let trigger = UNCalendarNotificationTrigger(dateMatching: calendarComponents, repeats: true)
let localNotification = UILocalNotification()
localNotification.alertBody = "Hey, you must go shopping, remember?"
localNotification.soundName = UILocalNotificationDefaultSoundName
}
I have a function scheduleFutureLocalNotifications() in Swift code that creates 64 UILocalNotification that fire in the future.
Originally the function was called at viewDidLoad(), but this caused delays when starting the app.
Next, the function was called during the active application, but this caused unpredictable pauses or lagging of the user interface.
Finally, the function was moved to trigger when the app transitions to the background after receiving a UIApplicationDidEnterBackground notification, but this causes iOS to briefly lag as the local notifications are prepared in the background. This appears more evident on older devices.
Question:
1 - How can I reduce lag and improve user interface responsiveness
creating local notifications?
2 - What better techniques can be employed to schedule the 64
notifications?
3 - What other better times could the function scheduleFutureLocalNotifications() be called?
Code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
scheduleFutureLocalNotifications()
}
func scheduleFutureLocalNotifications() {
// Remove all previous local notifications
let application = UIApplication.sharedApplication()
application.cancelAllLocalNotifications()
// Set new local notifications to fire over the coming 64 days
for nextLocalNotification in 1...64 {
// Add calendar day
let addDayComponent = NSDateComponents()
addDayComponent.day = nextLocalNotification
let calendar = NSCalendar.currentCalendar()
let nextDate = calendar.dateByAddingComponents(addDayComponent, toDate: NSDate(), options: [])
// Set day components for next fire date
let nextLocalNotificationDate = NSCalendar.currentCalendar()
let components = nextLocalNotificationDate.components([.Year, .Month, .Day], fromDate: nextDate!)
let year = components.year
let month = components.month
let day = components.day
// Set notification fire date
let componentsFireDate = NSDateComponents()
componentsFireDate.year = year
componentsFireDate.month = month
componentsFireDate.day = day
componentsFireDate.hour = 0
componentsFireDate.minute = 0
componentsFireDate.second = 5
let fireDateLocalNotification = calendar.dateFromComponents(componentsFireDate)!
// Schedule local notification
let localNotification = UILocalNotification()
localNotification.fireDate = fireDateLocalNotification
localNotification.alertBody = ""
localNotification.alertAction = ""
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.repeatInterval = NSCalendarUnit(rawValue: 0)
localNotification.applicationIconBadgeNumber = nextLocalNotification
application.scheduleLocalNotification(localNotification)
}
}
}
You can dispatch the function, asynchronously, onto another queue. This will ensure that the main queue isn't blocked performing the scheduling and will prevent the UI from becoming unresponsive:
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
scheduleFutureLocalNotifications()
}
}
I can do it manually with the following code:
var myDate:NSDateComponents = NSDateComponents()
myDate.year = 2015
myDate.month = 04
myDate.day = 20
myDate.hour = 12
myDate.minute = 38
myDate.timeZone = NSTimeZone.systemTimeZone()
var calendar:NSCalendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
var date:NSDate = calendar.dateFromComponents(myDate)!
var notification:UILocalNotification = UILocalNotification()
notification.category = "First Category"
notification.alertBody = "Hi, I'm a notification"
notification.fireDate = date
UIApplication.sharedApplication().scheduleLocalNotification(notification)
But how can I run it every hour or every day? Any idea?
First: add an extension to the Date class:
extension Date {
func currentTimeMillis() -> Int64 {
return Int64(self.timeIntervalSince1970 * 1000)
}
}
then call this function in the viewDidLoad():
func run24HoursTimer() {
let currentDate = Date()
let waitingDateTimeInterval:Int64 = UserDefaults.standard.value(forKey: "waiting_date") as? Int64 ?? 0
let currentDateTimeInterval = currentDate.currentTimeMillis()
let dateDiffrence = currentDateTimeInterval - waitingDateTimeInterval
if dateDiffrence > 24*60*60*1000 {
// Call the function that you want to be repeated every 24 hours here:
UserDefaults.standard.setValue(currentDateTimeInterval, forKey: "waiting_date")
UserDefaults.standard.synchronize()
}
}
There is a separate property on a local notification called repeatInterval. See reference
notification.repeatInterval = .Day
Also keep in mind to register in application delegate (didFinishLaunchingWithOptions method) for local notification (alert asking for permission will be presented for the first time). In Swift this will be (an example):
if UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:"))
{
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil))
}
I would also recommend setting time zone for the notification, could be like this (example):
notification.timeZone = NSTimeZone.localTimeZone()
Not sure about "run function every...". This will create a notification fired with the specified repeat interval. I found this tutorial helpful.
Use This :-
1). save your daily time in user defaults
2). set notification on time for next day
3). check in app delegate if time is passed or not
4). if it is passed then set next day notification
5). if you change time update user defaults
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: indentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: {
(errorObject) in
if let error = errorObject{
print("Error \(error.localizedDescription) in notification \(indentifier)")
}
})
You mean something like this?
let timer = NSTimer(timeInterval: 3600, target: self, selector: "test", userInfo: nil, repeats: false)
func test() {
// your code here will run every hour
}
Put all that code in one class. Much more info at #selector() in Swift?
//Swift >=3 selector syntax
let timer = Timer.scheduledTimer(timeInterval: 3600, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)
func test() {
// your code here will run every hour
}
Note: Time Interval is in seconds
Reference : How can I use NSTimer in Swift?
I'd like to let my app detect date even when iOS app is suspended(neither foreground nor background).
Can app use timer or function which get date in the suspended state?
This question may be simple question, but I couldn't find answer on the web.
Please let me know it is possible or not.
[Edit]
I want to run specified code at tomorrow midnight whenever app is any status.
Thanks in Advance!
This is OS X programming, but maybe you can adapt it.
So setup a timer:
var timer:NSTimer? = NSTimer(timeInterval:30.0, target: self, selector: "timeCheck:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSDefaultRunLoopMode)
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSEventTrackingRunLoopMode)
This sets up a timer to fire every 30 seconds. Then what happens when the timer fires:
func timeCheck(timer:UnsafePointer<NSTimer>)
{
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay , fromDate: date)
let year = components.year
let month = components.month
let day = components.day
// Do something with this
println("Today is \(day), \(month), \(year)")
}
This prints:
Today is 24, 12, 2014
Every 30 seconds.