call function periodically at random intervals in Swift - ios

I am trying to call a function 10 times at random intervals between them.
How can I achieve this?
I did come up with a method, but is is horribly ugly. It looks like this:
var counter = 0
NSTimer.scheduledTimerWithTimeInterval(arc4random_uniform(4)+2, target: self, selector: Selector("createNewTimer"), userInfo: nil, repeats: false)
func createNewTimer(){
// PERFORM STUFF YOU NEED TO
counter++
if counter <= 10{
NSTimer.scheduledTimerWithTimeInterval(arc4random_uniform(4)+2, target: self, selector: Selector("createNewTimer"), userInfo: nil, repeats: false)
}
}
Is there a nicer way of calling function at random intervals?

Try this in the playground. Hope it helps:
func after(delay: Double, block: () -> Void) {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
block()
}
}
func repeatBlock(counter: Int = 1, times: Int, block: () -> Void) {
after(Double(arc4random_uniform(4) + 2)) {
block()
if counter < times {
repeatBlock(counter + 1, times: times, block: block)
}
}
}
//client's code
var counter = 0
repeatBlock(times: 10) {
//your code here
print(NSDate())
}
sleep(100)

If you need a random intervals, then I'm afraid, the above is the best solution. If the time interval was same, then you could have set it to repeats:true. But not in you case.

Related

Set a timer with variable intervals in swift

I need to set a specific timer asynchronously after executing an action like this:
calling my function (sending http request)
10 seconds after, sending another request
20 seconds after 2), sending another one
40 seconds after 3), another one
then send every 60 seconds another one
At any moment, I must be able to cancel my timer. Firstable I thought using DispatchQueue, but I see several post saying that it's not possible to cancel it.
Some post suggest to use DispatchWorkItem ( how to stop a dispatchQueue in swift ) but I'm not sur it fit my need (unless adding a sleep(10,20,40,60...) in each loop but will it not impact asynchronous part?).
Another answer from this post suggest to use Timer instead ( scheduledTimerWithTimeInterval ) with repeats:false, and invalidate it after each loop, but I didn't undertand how to do the loop in this case. Actually, here's my code, that just send a request after 10 seconds:
private func start() {
timer?.invalidate()
if(self.PCount > self.Intervals.count){
self.value = self.pollingIntervals.count-1
} else {
self.Value = self.Intervals[self.pCount]
}
print("set timer with \(pollingValue) as interval")
timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(pollingValue), repeats: false, block: { timer in
self.sessionManager.sendHit()
self.pollingCount+=1
})
}
The current goal is to do something like coroutine in Kotlin, like it work with this code :
private val Intervals = longArrayOf(10000,20000,40000,60000)
private var Count = 0
private fun start() {
currentJob = GlobalScope.launch {
while (true) {
delay(Intervals[if (Count > Intervals.size) Intervals.size - 1 else Count]) // 10,20,40 then every 60
session.sendHit()
pollingCount++
}
}
}
I'm not sure what solution is the most appropriate to my project
Here is a basic idea on how to approach the problem
struct RequestMananger {
var timers: [Timer] = []
mutating func startSequence() {
var delay = 10.0
sendRequest()
timers.append(scheduleTimer(delay))
delay += 20
timers.append(scheduleTimer(delay))
delay += 40
timers.append(scheduleTimer(delay))
delay += 60
timers.append(scheduleTimer(delay, repeats: true))
}
private func scheduleTimer(_ delay: TimeInterval, repeats: Bool = false) -> Timer {
return Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { timer in
self.sendRequest()
})
}
func sendRequest() {
}
func cancelTimers() {
timers.forEach { $0.invalidate() }
}
}

I'm trying to use NSTimer in a tableview with mutliple timers

When I start a timer, however, it seems to repeatedly run, but it won't decrease the counter. I'm trying to pass the count variable through the counter into the selector, but it seems that the counter resets each time, instead of continuely decreasing. I'm new to programming, so while I'm hoping it's something silly, I might have everything wrong organizationally... my code is:
func timerDidEnd(timer: NSTimer) {
var timeCount = timer.userInfo as! Double
timeCount -= timeInterval
if timeCount <= 0 { //test for target time reached.
print("Timer = 0")
timer.invalidate()
} else { //update the time on the clock if not reached
print("Timer Count: \(timeCount)")
}
extension ViewController: TimerTableViewCellDelegate {
func startTimer(indexPath: NSIndexPath!) {
print("timer \(indexPath.row) button started")
var currentTimer = baseArray[indexPath.row]
currentTimer.timeCount = Double((currentTimer.duration[0] * 60) + (currentTimer.duration[1]) + currentTimer.duration[2])
currentTimer.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: "timerDidEnd:", userInfo: currentTimer.timeCount, repeats: true)
}
func stopTimer(indexPath: NSIndexPath!) {
let currentTimer = baseArray[indexPath.row]
print("Stop Timer")
currentTimer.timer.invalidate()
}
I figured it out- I updated my timer struct to include the proper time count, and this worked while passing in the indexPath as userInfo

Change Label Text at a Time Interval based on Character Count of a String within String Array

I'm trying to have a character count of a String element in an Array (buttonTappedOutput) determine the time before updating a UILabel (outputText.text). Immediately after the label is updated, I'd like the timer to start again based on the next String in the array, updating again and again for all String elements (in order from element 0 to X).
Why it's not working:
Right now, my for/in loop is doing it's thing, the timer is overwritten for each loop, and finally, the final set timer (based on length of the last String in the array) invokes updateOutputText and updates the output text to the last String in the array. I've also tried calling my updateOutputText function within the for/in loop and including a delay in my updateOutputText function, but have the same result. I basically need to "pause" the execution of the for/in after each loop until updateOutputText does it's thing, but is that really the best way to attack this issue? A pause?
Also, am I passing the local variable 'output' the right way (currentOutput = output)? Is there another/better way to access it outside of the for/in?
Here's my setup with the NSTimer:
#IBAction func eastPressed(sender: AnyObject) {
for output in trigger.currentDecisionPoint.buttonTappedOutput {
currentOutput = output
timer = NSTimer.scheduledTimerWithTimeInterval(Double(output.characters.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
}
func updateOutputText() {
outputText.text = currentOutput
return
}
With a delay in the updateOutputText function:
#IBAction func eastPressed(sender: AnyObject) {
for output in trigger.currentDecisionPoint.buttonTappedOutput {
currentOutput = output
updateOutputText()
//timer = NSTimer.scheduledTimerWithTimeInterval(Double(output.characters.count), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
}
func updateOutputText() {
delay(Double(currentOutput.characters.count)) { () -> () in
self.outputText.text = currentOutput
}
return
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
The way I would approach it is something like that (didn't test it though):
var index = 0
#IBAction func eastPressed(sender: AnyObject) {
firstOutput = trigger.currentDecisionPoint.buttonTappedOutput[index]
timer = NSTimer.scheduledTimerWithTimeInterval(Double(firstOutput.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
func updateOutputText() {
outputText.text = trigger.currentDecisionPoint.buttonTappedOutput[index]
if index < trigger.currentDecisionPoint.buttonTappedOutput.count - 1 {
nextOutput = trigger.currentDecisionPoint.buttonTappedOutput[index + 1]
timer = NSTimer.scheduledTimerWithTimeInterval(Double(nextOutput.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
index++
} else {
index = 0
}
}

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() {
}

NSTimer - how to delay in Swift

I have a problem with delaying computer's move in a game.
I've found some solutions but they don't work in my case, e.g.
var delay = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: nil, userInfo: nil, repeats: false)
I tried to use this with function fire but also to no effects.
What other possibilities there are?
Swift 3
With GCD:
let delayInSeconds = 4.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
// here code perfomed with delay
}
or with a timer:
func myPerformeCode() {
// here code to perform
}
let myTimer : Timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(self.myPerformeCode), userInfo: nil, repeats: false)
Swift 2
With GCD:
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
// here code perfomed with delay
})
or with a timer:
func myPerformeCode(timer : NSTimer) {
// here code to perform
}
let myTimer : NSTimer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("myPerformeCode:"), userInfo: nil, repeats: false)
With Swift 4.2
With Timer You can avoid using a selector, using a closure instead:
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { (nil) in
// Your code here
}
Keep in mind that Timer is toll-free bridged with CFRunLoopTimer, and that run loops and GCD are two completely different approaches.... e
In swift we can delay by using Dispatch_after.
SWift 3.0 :-
DispatchQueue.main.asyncAfter(deadline: .now()+4.0) {
alert.dismiss(animated: true, completion: nil)
}
How about using Grand Central Dispatch?
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html
Valfer has shown you how

Resources