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.
Related
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.
This question already has answers here:
How can I make a countdown timer like in a music player?
(2 answers)
Closed 7 years ago.
I'm trying to make an app that tell us the rest of time from the present time till one hour later.
This is the code but now it only has a function that tell us the countdown time by decreasing one second from the present time.
I'm thinking that I haven't definite the definition of the "cnt"
so that's why I'm thinking it doesn't work.
Can somebody tell me the reason and a solution?
import UIKit
class ViewController: UIViewController {
var cnt : Int = 0
var timer : NSTimer!//NSTimerというデフォルト機能から引っ張る
var myInt:Int = 0
override func viewDidLoad() {
let myDate: NSDate = NSDate()
let myCalendar: NSCalendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let myComponents = myCalendar.components([.Year, .Hour, .Minute, .Second],
fromDate: myDate) // myDate、すなわちNSDateから要素として引っ張り出してる
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "onUpdate:", userInfo: nil, repeats: true)//カウントダウンのインターバル
timer.fire()
var myStr: String = "\(myComponents.hour)"
myStr += "\(myComponents.minute)"
myStr += "\(myComponents.second)"
myInt = Int(myStr)! // toInt()がSwift2より無効になったようです。myInt=Str(my components,hour,minute,second)=現時刻
}
func onUpdate(timer : NSTimer){
cnt += 1//cnt+1=cnt,
let count = myInt - cnt //残り時間=現在時刻ー現在時刻に1時間足した時刻
print(count) // println()は、Swift2よりDeprecatedになりました。
}
}
It is difficult to understand what you're asking, but I will do my best.
In your viewDidLoad method, you're setting myInt to the integer representation of myStr. If the time is 18:30:50, myInt will be equal to 183050. That is not an appropriate representation of the time. Time is base 60, integers are base 10, for one thing. If you want to represent time as a single number, you can use timeIntervalSinceDate, or timeIntervalSinceReferenceDate or timeIntervalSince1970 to get the NSTimeInterval (ie. fractional seconds) representation of the date relative to a certain epoch either of your choosing or one built into Foundation.
Subtracting 1 from myInt each time the timer fires isn't going to give you an indication of the time remaining.
Also, NSTimer is not an accurate way to keep time. You should instead save the start date as a property and determine the time remaining based on timeIntervalSinceDate
e.g.
func onUpdate(timer : NSTimer){
let currentTime = NSDate()
let timeElapsed = currentTime.timeIntervalSinceDate(myDate)
println(timeElapsed)
}
If you want to show time elapsed in minutes, you can divide it by 60. You can look into NSDateComponentsFormatter to easily get a string representation of time intervals.
If you want the countdown to stop after an hour, then check for when timeElapsed is over 3600.
If you want it to show a countdown from 1 hour, then subtract the timeElapsed from 3600.
So, I am trying to make a UILocalNotification that fires multiple times per instance. That is, user chooses his iteration interval (i.e. every 2 hours) and also chooses the date when the notification would stop (i.e. 01.11.2015.) using the DateTimePicker.
I've looked for several answers and in every answers there was always one solution missing and since I am relatively new to iOS Dev, I don't know how to correctly implement them.
Most of the issues was the iteration value and the end date triggering properly. Can anyone help?
You would need to use something like NSTimer to schedule a repeated task like so (the time is in seconds):
var interval = 60.0 // user chosen interval
var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: Selector("helloWorld"), userInfo: nil, repeats: true)
func helloWorld()
{
println("Hello, World!")
}
Then you would also need to set up another timer similar to above which checks the date (in this example it does so every hour, but you can increase/decrease the accuracy by changing the interval). Once the dates match you then invalidate the previous timer to stop it repeating:
let chosenDate = "01.11.2015" // example date chosen with your DateTimePicker
var dateTimer = NSTimer.scheduledTimerWithTimeInterval(60.0 * 60, target: self, selector: Selector("checkDate"), userInfo: nil, repeats: true)
func checkDate() {
let date = NSDate()
println(date)
let formatter = NSDateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
let formattedDate = formatter.stringFromDate(date)
if formattedDate == chosenDate {
helloWorldTimer.invalidate() // disable previous timer
dateTimer.invalidate() // must also stop this timer as attempting to invalidate the other once already stopped would cause a crash
}
}
n.b. make sure your dates are both in the same format for comparison
n.b.2. this is written using Swift 1.2
Use NSTimer' scheduledTimerWithTimeInterval:target: selector:userInfo:nil repeats: to trigger and stop notifications based on user's selected values.
Running NSTimer in Background
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.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
Hey is that possible to change background image in an app using hour and minutes? For example sunrise at 7:30 and it will change background to a sunrise picture and time goes by it will change into morning at specific Hour and minutes too. If it can how to setup in swift. Thank you
If you're talking about changing the iOS background on the HomeScreen, no. Swift does not give you that much capability over iOS.
However, if you are talking about changing the background in your app to reflect the time of day, then it is absolutely possible. The Best way to go about it would be to check the time as soon as your app is opened using a function in your AppDelegate.swift file called
applicationDidBecomeActive
Once there, you need to capture the date and set it to a global variable (see here for more information regarding how to do this if you're unfamiliar)
after you have set the Date to a global variable, you will need to tell your application to change the background while the app is loading your view. This is just pseudocode of what the code will actually look like, but I hope it helps:
let sunRiseTime : Date = //insert date you would like the background to change here.
let noon : Date = //insert date for noon here
let sunriseBackgroundImage : UIImage = //insert image you would like for the sunrise here
let noonBackgroundImage : UIImage = //insert image you would like for noon here.
if(dateGlobalVariable >= sunriseTime){
self.backgroundImage = sunriseBackgroundImage
}else if(dateGlobalVariable >= noon{
self.backgroundImage = noonBackgroundImage
}
//continue this for as many images as you have.
hope this helps! let me know if you have any questions!
You can do it like that:
func setupTimer(hour: Int, minute: Int, second: Int, newColor: UIColor){
let calendar = Calendar.current.date(bySettingHour: hour, minute: minute, second: second, of: Date())
let timer = Timer(fireAt: calendar!, interval: 0, target: self, selector: #selector(changeBackground(_:)), userInfo: UIColor.gray, repeats: true)
RunLoop.main.add(timer, forMode: .common)
}
#objc func changeBackground(_ timer: Timer?){
if let newColor = timer?.userInfo as? UIColor {
DispatchQueue.main.async {
self.view.backgroundColor = newColor
}
}
}
Below method in AppDelgate will do the job.
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
This will get called when the application is opened/launched. change the app's background according to the time.