I'm using a UIDatePicker to allow users to choose a time for daily notifications in my app. However, it doesn't keep the value of the chosen time on the actual picker. How would I go about having the UIDatePicker reflect the time that they have chosen?
Setup of UIDatePicker and Notifications page
I don't know how you store your notification time, but you are responsible for setting the date picker to that time. For instance, here I am setting a picker to show the time exactly seven days ago:
let oneWeekAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
datePicker.setDate(oneWeekAgo, animated: true)
Your problem is since you are not saving the UIDatePicker time somewhere other than in the notification itself you are unsure of how to set it back to the UIDatePicker when it comes time to edit it. You could try something like this:
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
guard let notification = requests.first(where: { $0.identifier == "notInt" }) else { return }
guard let trigger = notification.trigger as? UNCalendarNotificationTrigger else { return }
notificationTime.date = trigger.nextTriggerDate()
}
... getting all pending notification requests, finding the one that matches the identifier you set and get the time from that. notificationTime above is the UIDatePicker.
Related
I have some code I want to run after a particular date/time has passed. For example, if I want the code to run 7 days from now and the user opens the app at any time on day 7 or after the code will run but if they open the app before the beginning of day 7 nothing happens. Timers in the main runloop work but only if the app is still running in the background. I need a method that will work even if the user kills the app.
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.
You can either set the date you want your app to "remember" on your local storage or web service. Then, when the user opens your app, compare that date to current device time to determine if you should execute your code.
First, save the current time when you want. You can set the key name however you want.
UserDefaults.standard.setValue(Date(), forKey: "rememberTime")
And every time I open the app, You compare the current time with the saved time.
To do so, I created a function that compares time.
extension Date {
func timeAgoSince() -> Bool {
let calendar = Calendar.current
let unitFlags: NSCalendar.Unit = [.day]
let components = (calendar as NSCalendar).components(unitFlags, from: self, to: Date(), options: [])
if let day = components.day, day >= 7 {
// Returns true if more than 7 days have passed.
return true
}
return false
}
}
Recall the previously saved time and use the time comparison function.
let beforeTime: Date = (UserDefaults.standard.object(forKey: "rememberTime") as? Date)!
if beforeTime.timeAgoSince() {
// more than seven days later
...
} else {
...
}
If you have a problem, please leave a comment !
You can use the below sample code:
override func viewDidLoad() {
super.viewDidLoad()
let nextCodeRunDate = Date() + (7 * 24 * 60 * 60) // 7 Days
if let savedDate = UserDefaults.standard.value(forKey: "NEXT_DATE") as? Date {
if Date() > savedDate {
UserDefaults.standard.setValue(nextCodeRunDate, forKey: "NEXT_DATE")
runYourCode()
}
}else {
// First time
UserDefaults.standard.setValue(nextCodeRunDate, forKey: "NEXT_DATE")
runYourCode()
}
}
func runYourCode() {
// Your code
}
I'm developing a social media app and I need to implement a countdown timer that will keep running, even after the app is shutdown completely. Basically exactly how Snaps in Snapchat behave. Is there a way I can do this within the app, or does this need to be done from the database that will contain app records, users, friends, etc? I'm fairly new to Swift, and I'm using Swift 4, so please keep answers to Swift 3/4.
Thanks!
A good question to ask yourself is if a Timer is necessary or if you could simply just store a start Date and then calculate the current time compared to that stored start Date and check if it exceeds your countdown time. If you wanted to demonstrate a countdown timer in a UI similar to a snap in snapchat disappearing perhaps the following simple example would help:
let allowableViewTimeInterval = TimeInterval(10) // 10 Seconds
let refreshTimeInterval = TimeInterval(1) // 1 Second refresh time on the label
let snapOpenedDate = Date() // the date they opened the snap
let label = UILabel(frame: .zero) // a label to display the countdown
let timer = Timer.scheduledTimer(withTimeInterval: refreshTimeInterval, repeats: true) {
let currentDate = Date()
let calendar = Calendar.current
let dateComponents = calendar.components(CalendarUnit.CalendarUnitSecond, fromDate: snapOpenedDate, toDate: currentDate, options: nil)
let seconds = dateComponents.second
label.text = "\(seconds)"
}
// If the countdown finishes or a user leaves the snap we need to make sure we invalidate the timer.
timer.invalidate()
You can then adapt this to your solution by storing the snapOpenedDate and looking it up again when an app resumes from background.
I want to retrieve steps from 1h ago. I don't need to do anything special, I just need to know how many steps a user has done since the last hour.
Even though my iPhone has some steps logged, the query to retrieve the number of steps returns "nil".
This is the code:
let calendar = Calendar.current //calendar now, to be used in calculating the h in the past
let beforeDate = calendar.date(byAdding: .hour, value: -1, to: Date())
let pedometer = CMPedometer() //define pedometer
if CMPedometer.isStepCountingAvailable() == true {print("steps available")}else{print("steps not available")}
pedometer.queryPedometerData(from: beforeDate!, to: Date(), withHandler: { (pedometerData, error) in
if let pedData = pedometerData{
self.dateLabel.text = "Steps:\(pedData.numberOfSteps)"
}else {
self.dateLabel.text = "error)"
print(beforeDate)
}
})
}
And this is the date format that I put in the query:
2018-03-16 12:59:17 +0000
What is wrong?
One possible problem is that your CMPedometer object is stored only in a local variable. pedometer.queryPedometerData runs asynchronously, so this object needs to persist long enough to fulfill the query. But it can't do that if it is a local variable; it vanishes before the data can even be fetched. Try making pedometer a persistent instance property instead.
Also be aware that you don't know what queue the data will be delivered on. You need to step out to the main queue in order to talk to the interface, and you are failing to do that.
I am trying to add a timer for when a user clicks a button it starts a timer for 24 hours and disables the button for the next 24 hours. After that it is enabled again. There is a few answers out there for things similar but not 100% useful for doing it in SWIFT.
The main problem I am having is that I want this to be specific for each user. So 24 hours for every click on that one user. So for example: If I 'like' something then you want be able to 'like' that particular thing again for 24 hours but can still 'like' a different thing?
Thanks
*
Works for Swift 3
*
I have a daily video Ad that my users can view to get extra cash. This is what I use to ensure they can only view it once a day.
1.) Create a function that will be called when the user triggers it.
func set24HrTimer() {
let currentDate = NSDate()
let newDate = NSDate(timeInterval: 86400, since: currentDate as Date)
UserDefaults.standard.setValue(newDate, forKey: "waitingDate")
print("24 hours started")
//disable the button
}
2.) Create a variable at the top of your file.
let todaysDate = NSDate()
3.) In the viewDidLoad or didMoveToView call:
if let waitingDate:NSDate = UserDefaults.standard.value(forKey: "waitingDate") as? NSDate {
if (todaysDate.compare(waitingDate as Date) == ComparisonResult.orderedDescending) {
print("show button")
}
else {
print("hide button")
}
}
You can do it by setting the actual date + 1 day and save it into your NSUserDefaults:.
So in your button-pressed method, you can do something like that:
//user pressed button:
func buttonPressed(){
//current date
let currentDate = NSDate()
let calendar = NSCalendar.currentCalendar()
//add 1 day to the date:
let newDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: 1, toDate: currentDate, options: NSCalendarOptions.allZeros)
NSUserDefaults.standardUserDefaults().setValue(newDate, forKey: "waitingDate")
//disable the button
}
And to check the time you can retrieve the information. I would recommend to check it inside the AppDelegatemethods like applicationDidFinishLaunchingWithOptions.
//call it whereever you want to check if the time is over
if let waitingDate:NSDate = NSUserDefaults.standardUserDefaults().valueForKey("waitingDate") as? NSDate{
let currentDate = NSDate()
//If currentDate is after the set date
if(currentDate.compare(waitingDate) == NSComparisonResult.OrderedDescending){
//reenable button
}
}
A few things to consider first.
How important is it that this button can not be subverted?
If you are depending on the device for its current time and date, then the user can always just move that forward one day in the device settings.
Do you want any behavior to happen outside of your application?
should the user be notified that the button is now enabled
Assuming you don't need strictly enforce the 24 hour period, and you don't want to notify the user (they can find out when they return to your app), then you have only to do a few things.
Get a timeStamp when the button is pressed, start an NSTimer for 24Hours, and save the timeStamp to NSUserDefaults.
//Assuming you have a method named enableButton on self
let timer = NSTimer.scheduledTimerWithTimeInterval(86400, target: self, selector: "enableButton", userInfo: nil, repeats: false)
NSUserDefaults.standardUserDefaults().setObject(NSDate(), forKey: "timeStamp")
Now if the user never leaves your app, your good. In real life they will, so you will need to check when you re-enter your app if you need to disable the button, based on the timeStamp and start a new timer for the amount of time that is left.
I am trying to run a particular function with background execution at a certain time each morning. Here's what I have figured out...
func getCurrentTime() { //Is called every minuite by a timer
let date = NSDate()
let formatter = NSDateFormatter()
formatter.timeStyle = .ShortStyle
var stringValue = formatter.stringFromDate(date)
if stringValue == "7:00 AM" {
sendPushAlert()//Defined elsewhere in code, executes desired code
}
}
However, that does not run in the background (e.g. when app not open), and seems like a clunky way to do things.
So, How do I run background execution at a certain time each morning?
Or, is there a better way to fetch data to show in a push notification?
Thanks!!!!!!
You can't force iOS to launch your app at a specific time.
If all you want to do is send a notification at a specific time, schedule a local notification. If the user opens your notification then your app will launch.