Do something every minute in Swift 4? [duplicate] - ios

How can I run a function every minute?
In JavaScript I can do something like setInterval, does something similar exist in Swift?
Wanted output:
Hello World once a minute...

var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
func sayHello()
{
NSLog("hello World")
}
Remember to import Foundation.
Swift 4:
var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)
#objc func sayHello()
{
NSLog("hello World")
}

If targeting iOS version 10 and greater, you can use the block-based rendition of Timer, which simplifies the potential strong reference cycles, e.g.:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // just in case you had existing `Timer`, `invalidate` it before we lose our reference to it
timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
// do something here
}
}
func stopTimer() {
timer?.invalidate()
}
// if appropriate, make sure to stop your timer in `deinit`
deinit {
stopTimer()
}
While Timer is generally best, for the sake of completeness, I should note that you can also use dispatch timer, which is useful for scheduling timers on background threads. With dispatch timers, since they're block-based, it avoids some of the strong reference cycle challenges with the old target/selector pattern of Timer, as long as you use weak references.
So:
var timer: DispatchSourceTimer?
func startTimer() {
let queue = DispatchQueue(label: "com.domain.app.timer") // you can also use `DispatchQueue.main`, if you want
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: .seconds(60))
timer!.setEventHandler { [weak self] in
// do whatever you want here
}
timer!.resume()
}
func stopTimer() {
timer = nil
}
For more information, see the the Creating a Timer section of Dispatch Source Examples in the Dispatch Sources section of the Concurrency Programming Guide.
For Swift 2, see previous revision of this answer.

If you can allow for some time drift here's a simple solution executing some code every minute:
private func executeRepeatedly() {
// put your code here
DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
self?.executeRepeatedly()
}
}
Just run executeRepeatedly() once and it'll be executed every minute. The execution stops when the owning object (self) is released. You also can use a flag to indicate that the execution must stop.

Here's an update to the NSTimer answer, for Swift 3 (in which NSTimer was renamed to Timer) using a closure rather than a named function:
var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
(_) in
print("Hello world")
}

You can use Timer (swift 3)
var timer = Timer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)
In selector() you put in your function name

In swift 3.0 the GCD got refactored:
let timer : DispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timer.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timer.setEventHandler
{
NSLog("Hello World")
}
timer.resume()
This is specially useful for when you need to dispatch on a particular Queue. Also, if you're planning on using this for user interface updating, I suggest looking into CADisplayLink as it's synchronized with the GPU refresh rate.

Here is another version algrid's answer with an easy way to stop it
#objc func executeRepeatedly() {
print("--Do something on repeat--")
perform(#selector(executeRepeatedly), with: nil, afterDelay: 60.0)
}
Here's an example of how to start it and stop it:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
executeRepeatedly() // start it
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NSObject.cancelPreviousPerformRequests(withTarget: self) // stop it
}

timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true, block: myMethod)
func myMethod(_:Timer) {
...
}
or
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
...
}
make sure to invalid the timer at some point like your time is no longer visible, or you object is deist

Related

How to properly remove Timer instance

I am using Timer instance in my project.
I create and add an instance to RunLoop
timer = Timer(timeInterval: 0.1, repeats: true, block: { (timer) in
print("Hello")
})
if let timer = timer {
RunLoop.current.add(timer, forMode: .commonModes)
}
Now I am thinking about how to properly stop and remove Timer:
Should I trust in ARC and don't care about it?
Maybe deinit implementation is need. If is it true, what should be inside
deinit {
longPressTimer?.invalidate()
longPressTimer = nil
}
invalidate is nesseccery? = nil ?
I have read serval threads from Stack about it but answers are contradictory. Could someone tell me which way is properly and explain me why?
You need both invalidate() and = nil. Unlike other objects, a timer DOES NOT get deallocated when its retain count reaches zero unless it's stopped (invalidated).
Say, if you have a repeating timer which is currently active. when you set timer = nil, it still persists in memory and keeps triggering the action (probably the iOS has some kinds of mechanism to keep it alive as long as it's still active). So the rule of thumb is: Always call invalidate() and set the timer to nil when you want to get rid of it.
If timer is repeating it won't invalidate as long as it's target is in memory.
The best solution for me was to use this class - which will observe target, and it target is deallocate it will invalidate itself.
final class WeakTimer {
private weak var timer: Timer?
private weak var target: AnyObject?
private let action: (Timer) -> Void
private init(timeInterval: TimeInterval,
target: AnyObject,
repeats: Bool,
userInfo: Any?,
action: #escaping (Timer) -> Void) {
self.target = target
self.action = action
self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(fire(timer:)), userInfo: userInfo, repeats: repeats)
RunLoop.main.add(self.timer!, forMode: .commonModes)
}
class func scheduledTimer(timeInterval: TimeInterval,
target: AnyObject,
userInfo: Any?,
repeats: Bool,
action: #escaping (Timer) -> Void) -> Timer {
return WeakTimer(timeInterval: timeInterval,
target: target,
repeats: repeats,
userInfo: userInfo,
action: action).timer!
}
#objc fileprivate func fire(timer: Timer) {
if target != nil {
action(timer)
} else {
timer.invalidate()
}
}
}
First check whether timer exist? if yes then invalidate it like
if timer!= nil {
timer.invalidate()
}
Keep in mind that if you use a timer in any VC invalidate it when you leave that VC(View Controller)

Timer.scheduledTimer does not work in Swift 3

I want to call the method func adjustmentBestSongBpmHeartRate() every 1.1 second. I used Timer, but it doesn't work. I have read the document and found a lot of sample code, it still does work! Is there anything I missed?
timer = Timer.scheduledTimer(timeInterval: 1.1, target: self, selector: #selector(self.adjustmentBestSongBpmHeartRate), userInfo: nil, repeats: false)
timer.fire()
func adjustmentBestSongBpmHeartRate() {
print("frr")
}
I found that creating the timer in an OperationQueue Operation did not work. I assume this is because there is no runloop.
Therefore, the following code fixed my problem:
DispatchQueue.main.async {
// timer needs a runloop?
self.timeoutTimer = Timer.scheduledTimer(timeInterval: self.timeout, target: self, selector: #selector(self.onTimeout(_:)), userInfo: nil, repeats: false)
}
Timer methods with a selector are supposed to have one parameter: The timer itself. Thus your code should really look like this: 1
Timer.scheduledTimer(timeInterval: 1.1,
target: self,
selector: #selector(self.adjustmentBestSongBpmHeartRate(_:),
userInfo: nil,
repeats: false)
#objc func adjustmentBestSongBpmHeartRate(_ timer: Timer) {
print("frr")
}
Note that if your app only runs on iOS >= 10, you can use the new method that takes a block to invoke rather than a target/selector. Much cleaner and more type-safe:
class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: #escaping (Timer) -> Void) -> Timer
That code would look like this:
timer = Timer.scheduledTimer(withTimeInterval: 1.1,
repeats: false) {
timer in
//Put the code that be called by the timer here.
print("frr")
}
Note that if your timer block/closure needs access to instance variables from your class you have to take special care with self. Here's a good pattern for that sort of code:
timer = Timer.scheduledTimer(withTimeInterval: 1.1,
repeats: false) {
//"[weak self]" creates a "capture group" for timer
[weak self] timer in
//Add a guard statement to bail out of the timer code
//if the object has been freed.
guard let strongSelf = self else {
return
}
//Put the code that be called by the timer here.
print(strongSelf.someProperty)
strongSelf.someOtherProperty = someValue
}
Edit (updated 15 December)
1: I should add that the method you use in the selector has to use Objective-C dynamic dispatch. In Swift 4 and later, the individual methods you reference must be tagged with the #objc tag. In previous versions of Swift you could also declare the entire class that defines the selector with the #objc qualifier, or you could make the class that defined the selector a subclass of NSObject or any class that inherits from NSOBject. (It's quite common to define the method the timer calls inside a UIViewController, which is a subclass of NSObject, so it used to "just work".
Swift 3
In my case it worked after I added to my method the #obj prefix
Class TestClass {
private var timer: Timer?
func start() {
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(handleMyFunction), userInfo: nil, repeats: false)
}
func stop() {
guard timer != nil else { return }
timer?.invalidate()
timer = nil
}
#objc func handleMyFunction() {
// Code here
}
}
Try this -
if #available(iOS 10.0, *) {
self.timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.update()
})
} else {
self.timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.update), userInfo: nil, repeats: false)
}
Mostly the problem must have been because of iOS version of mobile.
Swift 5, Swift 4 Simple way only call with Dispatch Queue Async
DispatchQueue.main.async
{
self.andicator.stopAnimating()
self.bgv.isHidden = true
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { _ in
obj.showAlert(title: "Successfully!", message: "Video save successfully to Library directory.", viewController: self)
})
}
I have solved the question asked by myself.
I'm using apple watch to control my iphone app.
I try to press a button on apple watch to present a new viewcontroller on iphone.
When I write Timer in override func viewDidLoad(), Timer doesn't work. I move Timer to override func viewWillAppear() it works.
I think maybe there's something wrong with controlling by apple watch
I found that if you try to initialize the timer directly at the class-level, it won't work if you're targeting a selector in that same class. When it fires, it can't find the selector.
To get around this, I only initialize the timer after the object containing the selector has been initialized. If it's in the same class, put the initialization code in the ViewDidLoad or similar. Just not in the initializer. Then it will work. No dispatch queue needed.
Also, you do not need to use a selector that accepts the timer as a parameter. You can, but contrary to the answer with a ton of votes, that's not actually true, or more specifically, it works fine for me without it, just as you have it without it.
By the way, I think the reason the dispatch queue worked is because you're forcing the timer to be created after the object was initializing, confirming my above statement.
let timer:Timer?
override func viewDidLoad(){
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1.1, target: self, selector: #selector(adjustmentBestSongBpmHeartRate), userInfo: nil, repeats: false)
timer.fire()
}
func adjustmentBestSongBpmHeartRate() {
print("frr")
}
Note: This is code typed from memory, not copied from Xcode so it may not compile, but hopefully you get the idea.
Swift3
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.compruebaConexion), userInfo: nil, repeats: true)
my two cents.
I read about "didLoad" and when invoking it.
so we can use a delay:
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
startTimer()
}
final func killTimer(){
self.timer?.invalidate()
self.timer = nil
}
final private func startTimer() {
// make it re-entrant:
// if timer is running, kill it and start from scratch
self.killTimer()
let fire = Date().addingTimeInterval(1)
let deltaT : TimeInterval = 1.0
self.timer = Timer(fire: fire, interval: deltaT, repeats: true, block: { (t: Timer) in
print("hello")
})
RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
}

swift invalidate timer doesn't work

I have this problem for a few days now and I don't get what I am doing wrong.
My application is basically just creating some timers. I need to stop them and create new ones. But at the moment stopping them doesn't work.
self.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target:self, selector: "timerDidEnd:", userInfo: "Notification fired", repeats: false)
That's my timer
func timerDidEnd(timer:NSTimer){
createUnrepeatedAlarmWithUpdateInterval()
}
Because my timer didn't want to stop I am currently using the unrepeated timer and start it myself after it stopped.
func stopAlarm() {
if self.timer != nil {
self.timer!.invalidate()
}
self.timer = nil
self.timer = NSTimer()
}
And that's how I stop my timer.
alarmManager.stopAlarm()
alarmManager.createUnrepeatedAlarmWithUpdateInterval()
I call the stopAlarm() function before creating a new timer.
I really don't know what I am doing wrong so I appreciate every answer :)
class AlarmManager: ViewController{
private var timer : NSTimer?
private var unrepeatedTimer : NSTimer?
private let notificationManager = NotificationManager()
private var current = NSThread()
private let settingsViewController = SettingsViewController()
func createRepeatedAlarmWithUpdateInterval(){
var timeInterval:NSTimeInterval = settingsViewController.getUpdateIntervalSettings()
if timer == nil{
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "repeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: true)
}
}
func repeatedTimerDidEnd(repeatedTimer:NSTimer){
ConnectionManager.sharedInstance.loadTrainings(settingsViewController.getServerSettings())
createUnrepeatedAlarm(10)
}
func createUnrepeatedAlarm(timeInterval:Double){
unrepeatedTimer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "unrepeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: false)
}
func unrepeatedTimerDidEnd(unrepeatedTimer:NSTimer){
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMRATINGREMINDER)
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMREMINDER)
print("UnrepeatedAlarm ended")
}
func stopAlarm(){
print("StopAlarm triggered")
if (timer != nil)
{
print("stoptimer executed")
timer!.invalidate()
timer = nil
}
if (unrepeatedTimer != nil)
{
unrepeatedTimer!.invalidate()
unrepeatedTimer = nil
}
}
}
Thats the whole code of this class. Maybe that helps :D
The usual way to start and stop a timer safely is
var timer : Timer?
func startTimer()
{
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}
func stopTimer()
{
timer?.invalidate()
timer = nil
}
startTimer() starts the timer only if it's nil and stopTimer() stops it only if it's not nil.
You have only to take care of stopping the timer before creating/starting a new one.
Make sure you're calling invalidate on the same thread as the timer.
From the documentation:
Special Considerations
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.
https://developer.apple.com/documentation/foundation/nstimer/1415405-invalidate?language=objc
Something that's not really covered by the previous answers is that you should be careful your timer isn't scheduled multiple times.
If you schedule a timer multiple times without first invalidating it, it'll end up scheduled on multiple run loops, and invalidating it then becomes nigh impossible.
For me, it happened when calling my scheduleTimer() function in separate functions in my view controller's life cycle (viewWillAppear, viewDidAppear, ...)
So in short, if you aren't sure (or you cannot guarantee) your Timer is only scheduled once, just always invalidate it first.
I have tried every possible solution found but not able to resolve that at the end I have set repeat "false" while initialising timer like below
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.methodname), userInfo: nil, repeats: false)
And need to add above line in my selector method for whatever the condition for which I wanted to repeat the time.
For example:-
My requirement is I want to repeatedly call some method until one condition satisfied. So instead of adding repeats true I set it false as repeat true does not invalidate timer in my case.
I have added below in my viewdidload method
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
in selector function I added below code
#objc func method{
if condition not matched{
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
}
else{
// once you are here your timer invalidate automatically
}
}
Hope this will solve your problem
For Swift 5 Xcode 12.4 there is example to use timer:
class MyController: UIViewController {
private id: Float;
func setValue(_ value: Float, withAnimation: Bool) {
let step: Float = value / 200
var current: Float = withAnimation ? 0.0 : value
let _ = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: withAnimation) { timer in
DispatchQueue.main.async {
self.id = current
current += step
if current > value || withAnimation == false {
self.id = current
timer.invalidate()
}
}
}
}
}

while loop that checks it's condition every second in swift

How does one create a while loop that checks it's condition every second?
maybe something like this:
while (isConditionSatisfied){
// wait for 1 second and than check again
}
EDIT: The system calls this function bannerViewDidLoadAd at random times. If it calls it at inappropriate time(condition is unsatisfied-my app is performing some other animation), I would like to defer its implementation(just an UIView animation) until the condition is satisfied(my app has finished animating, now the implementation should be executed). I was thinking I could check the condition in a while loop every second, but as you guys said..this is a bad idea.
Using while loop like this will create an infinite loop actually. You should use Timer().
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
And then
#objc
func loop() {
if yourCondition {
// your code here
timer.invalidate()
}
}
Make sure you declare timer with your other variable declarations, so it can be invalidated once your condition has been met:
var timer: Timer!
Swift loop with interval
iOS 10
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
if someCondition {
timer.invalidate()
}
}
*withTimeInterval - number of seconds
you can do using NSTimer like this one
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(yourMethodName) userInfo:nil repeats:YES];
I just put together a Swift NSObject extension that would allow you to do this. It uses a delay function to allow you to wait until certain conditions are met or you cancel it.
https://gist.github.com/d3signerd/795a9dced39cbee056010d5629d9ca06
// Wait Example
waitWhile( { [weak self] () -> condition_parameters in
if let weakSelf = self {
return ( conditionMet: `criteria to meet`, cancel: false )
}
else {
return ( conditionMet: false, cancel: true )
}
}, completion: { [weak self] in
// Wait completion code goes here...
})
// Delay Example
private var cancelableDelayClosure:dispatch_cancelable_closure? = nil
cancelableDelayClosure = delay( 1.0, closure: { () -> () in
// Delay completion code here...
})
cancelDelay( &cancelableDelayClosure )
updated code for swift,
let timer = Timer.scheduledTimer(
timeInterval: 1.0, target: self, selector: #selector(Your_current_vc.yourTask),
userInfo: nil, repeats: true)
func yourTask() {
}

Make a service call at regular interval of time in swift

I am new to swift programming and I don't know how to call a method at regular interval of time. I have a demo app for service call but i don't know how can i call it at regular interval of time.
You can create an object of NSTimer() and call a function on definite time interval like this:
var updateTimer = NSTimer.scheduledTimerWithTimeInterval(15.0, target: self, selector: "callFunction", userInfo: nil, repeats: true)
this will call callFunction() every 15 sec.
func callFunction(){
print("function called")
}
Here is a simple example with start and stop functions:
private let kTimeoutInSeconds:NSTimeInterval = 60
private var timer: NSTimer?
func startFetching() {
self.timer = NSTimer.scheduledTimerWithTimeInterval(kTimeoutInSeconds,
target:self,
selector:Selector("fetch"),
userInfo:nil,
repeats:true)
}
func stopFetching() {
self.timer!.invalidate()
}
func fetch() {
println("Fetch called!")
}
If you get an unrecognized selector exception, make sure your class inherits from NSObject or else the timer's selector won't find the function!
Timer variant with a block (iOS 10, Swift 4)
let timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { (timer) in
print("I am called every 5 seconds")
}
Do not forget call invalidate method
timer.invalidate()
GCD approach (will tend to drift a bit late over time)
func repeatMeWithGCD() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
print("I am called every 5 seconds")
self.repeatMeWithGCD()//recursive call
}
}
Do not forget to create a return condition to prevent stackoverflow error

Resources