OSX/Swift: Call function at a specific date/time
You could try Grand Central Dispatch. Specifically use dispatch_walltime() to create a dispatch_time_t
representing the time you want the job to run and then use dispatch_after() to submit the job to Grand Central Dispatch for execution at the specified time.
iOS: execute code AFTER a particular date
Your best option is to store it as local data
Even though you only want the code to run once, the overhead is so low, the "check" will not impact the speed or feel of the application. Also this will allow you to run additional checks .. If someone deletes the app, for instance, and leaves the local storage behind. If they re-install you could theoretically "remember" that the application has been installed, and said code has already run (until the user clears application data)
Something like:
//Globally set key
struct defaultsKeys {
static let keyDate = "dateKey"
}
// Set the date in local storage
let defaults = UserDefaults.standard
defaults.set("Your Date String", forKey: defaultsKeys.dateKey)
// Get the date from local storage
let defaults = UserDefaults.standard
if let stringDate = defaults.string(forKey: defaultsKeys.dateKey) {
print(stringDate)
// Do your date comparison here
}
Very few lines of code, and even though the check happens every time the application starts .. The overhead is negligible.
Call a function one time per day at a specific time Swift
You cannot guarantee that your app is running at that time, each day. the phone could of lost all of its battery power, app might not be running... etc etc.
But this is just a logical problem... If you want to refresh the data after a certain time each day, you should just store a last refresh time in UserDefaults, and if that time was more than your required expiry time (in this case 24h) then refresh the data and update the last updated time.
So if you have an active user, as soon as the 24 hour time difference expires it would refresh, or if a users phone was off for a few days (for whatever reason) your app would still know it needs to refresh the data once the user comes back to the app.
EDIT: Added code example
class RefreshManager: NSObject {
static let shared = RefreshManager()
private let defaults = UserDefaults.standard
private let defaultsKey = "lastRefresh"
private let calender = Calendar.current
func loadDataIfNeeded(completion: (Bool) -> Void) {
if isRefreshRequired() {
// load the data
defaults.set(Date(), forKey: defaultsKey)
completion(true)
} else {
completion(false)
}
}
private func isRefreshRequired() -> Bool {
guard let lastRefreshDate = defaults.object(forKey: defaultsKey) as? Date else {
return true
}
if let diff = calender.dateComponents([.hour], from: lastRefreshDate, to: Date()).hour, diff > 24 {
return true
} else {
return false
}
}
}
You don't really have to make this a new class or a singleton like I have. It was just easier to keep everything nicely contained for the sake of the answer.
So this will work that if there is no refresh date, or if the number of hours since the last one is greater than 24 then it will update the data and update the last refresh date/time. using the completion it will also return whether it updated data or not.
You could also add a check in here that the current hour of the day is >= 16
if required.
EDIT 2: User selectable hour
private func isRefreshRequired(userPickedHour: Int = 16) -> Bool {
guard let lastRefreshDate = defaults.object(forKey: defaultsKey) as? Date else {
return true
}
if let diff = calender.dateComponents([.hour], from: lastRefreshDate, to: Date()).hour,
let currentHour = calender.dateComponents([.hour], from: Date()).hour,
diff >= 24, userPickedHour <= currentHour {
return true
} else {
return false
}
}
In this modified isRefreshRequired function you can pass a value for the hour and see whether it has been at least 24 hours since the last refresh and that the current hour is or is greater than the users selected hour.
This doesn't mean it will run at say for example exactly 16:00 by the way. it will run when the user loads that screen and the rules pass (min 24 hours passed, currentHour is >= userSelected hour)
EDIT 3: how to call
class ViewController: UIViewController {
let refreshManager = RefreshManager.shared
override func viewDidLoad() {
super.viewDidLoad()
refreshManager.loadDataIfNeeded() { success in
print(success)
}
}
}
EDIT: Changed logic
if let diff = calender.dateComponents([.day], from: lastRefreshDate, to: Date()).day,
let currentHour = calender.dateComponents([.hour], from: Date()).hour,
diff >= 1, userPickedHour <= currentHour {
return true
} else {
return false
}
Recurring function at date/time
This may be pretty heavy to implement (i.e. not straightforward), but if you want a task to run even if your app is terminated, you might need to consider writing your own LaunchAgent.
The trick here would be for the agent to be able to interact with your application (retrieving or sending shared information).
iOS :Call a method in specific time
Actually your code works fine but it needs refreshing ... for example you can use NSTimer
to refresh your method :
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(updateNewDate) userInfo:nil repeats:YES];
How to get the current time as datetime
Update for Swift 3:
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
I do this:
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute, fromDate: date)
let hour = components.hour
let minutes = components.minute
See the same question in objective-c How do I get hour and minutes from NSDate?
Compared to Nate’s answer, you’ll get numbers with this one, not strings… pick your choice!
Delaying function in swift
You can use GCD (in the example with a 10 second delay):
Swift 2
let triggerTime = (Int64(NSEC_PER_SEC) * 10)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in
self.functionToCall()
})
Swift 3 and Swift 4
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0, execute: {
self.functionToCall()
})
Swift 5 or Later
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
//call any function
}
Related Topics
Does Cidetector for Other Barcode Types
Implement a Custom Staggeregrid in UIview Like Etsy App in Swift
Didupdateheading Not Called in Swift
Optional in Swift, Return Count of Array
Mkpolyline Broken When Using Type Satelliteflyover
Swiftui List .Ondelete: Index Out of Range
Audiokit Seems to Receive Only The First Three Numbers of Sysex Midi Messages
Using UIviewrepresentable to Wrap Marqueelabel View
Social Framework Is Deprecated in iOS11
How to Use Frame to Set X Position in Loop on Swift
Abstract Class and Abstract Function in Swift
iOS Coredata Compatible with Both iOS 9 and iOS 10
Firebase Authentication: Linking Multiple Accounts in Swift
Include Inheritance Constraint in Swift Generic Types
Can't Hide Status Bar in Avplayerviewcontroller's Portrait Mode
How to Show an Alert in Swift UIalertview Not Working
Swift Custom Response Serializer Is Returning Images at Random